package server.business;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Timer;

import server.NetworkService;
import server.domain.Auction;

/**
 * Manages the auctions.
 * 
 * @author Lukas
 *
 */
public class AuctionManager {
	private HashMap<Integer, Auction> auctions = new HashMap<Integer, Auction>();
	private int auctionNr = 0;
	private final NetworkService service;
	private final Timer timer;
	
	/**
	 * @param service The network service that provides udp-notifications-delivery.
	 */
	public AuctionManager(NetworkService service) {
		this.service = service;
		timer = new Timer();
	}
	
	/**
	 * Creates a new auction.
	 * @param owner The creator of the auction.
	 * @param description The description for the auction.
	 * @param duration How long the auction shall be available.
	 * @return The message for the owner.
	 */
	public synchronized String newAuction(String owner, String description, int duration) {
		long delay = (long)duration * 1000l;
		Date expiration = new Date(System.currentTimeMillis() + delay);
		AuctionStopper task = new AuctionStopper(this, service, ++auctionNr);
		timer.schedule(task, delay);
		auctions.put(auctionNr, new Auction(owner, description, expiration));
		return "An auction '" + description + "' with id " + auctionNr +
				" has been created and will end on " + expiration + ".";
	}
	
	/**
	 * Creates a new bid.
	 * @param id The id of the auction.
	 * @param bid The new bid.
	 * @param bidder The bidder.
	 * @return The message for the bidder.
	 */
	public synchronized String newBid(int id, int bid, String bidder) {
		Auction a = auctions.get(id);
		if (a == null) {
			return "There is no active auction with the id " + id + "!";
		}
		if (a.getBid() < bid) {
			service.sendMessage(a.getHighestBidder(), "!new-bid " + a.getDescription());
			a.setBid(bid);
			a.setHighestBidder(bidder);
			return "You successfully bid with " + convertPrice(bid) + " on '" + a.getDescription() + "'.";
		}
		return "You unsuccessfully bid with " + convertPrice(bid) + " on '" + a.getDescription() + "'." +
				" Current highest bid is " + convertPrice(a.getBid()) + ".";
	}
	
	/**
	 * Removes an auction from the list of active auctions.
	 * @param id The id of the auction.
	 * @return The removed auction; <tt>null</tt>, if there was no auction with the given id.
	 */
	public synchronized Auction removeAuction(int id) {
		return auctions.remove(id);
	}
	
	/**
	 * @return A list of all active auctions.
	 */
	public synchronized String listAuctions() {
		String list = "";
		Iterator<Entry<Integer, Auction>> iter = auctions.entrySet().iterator();
		while (iter.hasNext()) {
			Entry<Integer, Auction> e = iter.next();
			Auction a = e.getValue();
			list += e.getKey() + ". ";
			list += "'" + a.getDescription() + "' ";
			list += a.getOwner() + " ";
			list += a.getExpiration() + " ";
			list += convertPrice(a.getBid()) + " ";
			list += a.getHighestBidder() + "\n";
		}
		return list;
	}

	/**
	 * Converts the int-price into String.
	 * @param price The price.
	 * @return The price with a point.
	 */
	public String convertPrice(int price) {
		String result = Integer.toString(price);
		int split = result.length() - 2;
		for (; split < 1; split++) {
			result = "0" + result;
		}
		return result.substring(0, split) + "." + result.substring(split);
	}
	
	/**
	 * Converts the string of a price into int.
	 * @param price The price with a decimal point.
	 * @return The price.
	 */
	public int convertPrice(String price) {
		int result = 0;
		int multiplier = 1;
		boolean noPoint = true;
		for (int i = price.length() - 1; i >= 0; i--) {
			char c = price.charAt(i);
			if (c < '0' || c > '9') {
				if (c == '.' && noPoint) {
					noPoint = false;
					result = (result * 100) / multiplier;
					multiplier = 100;
				} else {
					throw new IllegalArgumentException();
				}
			} else {
				result += (c - '0') * multiplier;
				multiplier *= 10;
			}
		}
		if (noPoint) {
			result *= 100;
		}
		return result;
	}
	
	/**
	 * Stops the timer.
	 */
	public void end() {
		timer.cancel();
	}
}
